package com.mixsmart.config;

import com.mixsmart.config.bean.ProjectInfo;
import com.mixsmart.constant.IMixConstant;
import com.mixsmart.security.SecurityUtils;
import com.mixsmart.utils.LoggerUtils;
import com.mixsmart.utils.StringUtils;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

/**
 * 初始化系统配置文件
 * @author 乌草坡
 * @since 1.0
 */
public class SystemConfig extends AbstractConfig {

	/**
	 * 系统配置文件
	 */
	public static final String SYS_CONFIG_FILE = "/sysconfig.properties";
	
	private static SystemConfig systemInstance;

	private static final Map<String, SystemConfig> configInstanceMap = new HashMap<>();
	
	private final String sysConfigPath;
	
	private long lastModifyTime = 0;
	
	private Properties prop = null;

	private final String key;
	
	private SystemConfig(String sysConfigPath, boolean isSystemConfig) {
		if(StringUtils.isNotEmpty(sysConfigPath)) {
			this.sysConfigPath = sysConfigPath;
		} else {
			this.sysConfigPath = SYS_CONFIG_FILE;
		}
		key = SecurityUtils.md5(sysConfigPath);
		if(isSystemConfig) {
			systemInstance = this;
		}
		init();
	}
	
	private SystemConfig() {
		this(SYS_CONFIG_FILE, true);
	}
	
	/**
	 * 重新加载配置文件
	 */
	private void reloadConfig() throws IOException {
		String devModel = systemInstance.getValue("project.devModel");
		if(StringUtils.isNotEmpty(devModel) && IMixConstant.PROJECT_DEV_MODEL.equals(devModel)) {
			ConfigFileInfo configFile = getConfigFile();
			if(null != configFile) {
				//判断文件是否被修改过，如果文件已修改，则重新初始化文件
				if(configFile.lastModifyTime() > lastModifyTime) {
					reInit();
				}
			}
		}
	}
	
	/**
	 * 获取指定配置文件的配置实例
	 * @return SystemConfig 返回配置实例对象
	 */
	public static SystemConfig getInstance(String configPath) {
		boolean isSystemConfig = false;
		if(StringUtils.isEmpty(configPath)) {
			configPath = SYS_CONFIG_FILE;
			isSystemConfig = true;
		}
		String key = SecurityUtils.md5(configPath);
		SystemConfig instance = configInstanceMap.get(key);
		if(null != instance) {
			try {
				instance.reloadConfig();
			} catch (Exception ex) {
				LoggerUtils.error(getInstance().logger, "重新加载配置文件异常", ex);
			}
			return instance;
		}
		synchronized (key.intern()) {
			instance = configInstanceMap.get(key);
			if (null == instance) {
				instance = new SystemConfig(configPath, isSystemConfig);
				configInstanceMap.put(key, instance);
			}
		}
		return instance;
	}

	/**
	 * 获取默认系统配置实例
	 * @return SystemConfig 返回系统配置实例对象
	 */
	public synchronized static SystemConfig getInstance() {
		return getInstance(null);
	}

	/**
	 * 获取配置文件
	 * @return File 返回配置文件；如果没有找到配置文件，返回null
	 */
	private ConfigFileInfo getConfigFile() throws IOException {
		String path = System.getProperty("user.dir");
		String configPath = path + "/config" + this.sysConfigPath;
		File file = new File(configPath);
		boolean isFirstInit = (null == configInstanceMap.get(key));
		if(file.exists()) {
			if(isFirstInit) {
				LoggerUtils.trace(logger, "找到的配置文件为：{}", file.getAbsolutePath());
			}
			return new ConfigFileInfo(configPath, file.lastModified(), new FileInputStream(file));
		} else {
			configPath = path + this.sysConfigPath;
			file = new File(configPath);
			if(file.exists()) {
				if(isFirstInit) {
					LoggerUtils.trace(logger, "找到的配置文件为：{}", file.getAbsolutePath());
				}
				return new ConfigFileInfo(configPath, file.lastModified(), new FileInputStream(file));
			}
			URL url = this.getClass().getResource(this.sysConfigPath);
			if(null != url) {
				if(isFirstInit) {
					LoggerUtils.trace(logger, "找到的配置文件为：{}", url.getFile());
				}
				return new ConfigFileInfo(url.getFile(), new File(url.getFile()).lastModified(), url.openStream());
			}
		}
		return null;
	}

	record ConfigFileInfo(String filePath, long lastModifyTime, InputStream inputStream) {

	}

	/**
	 * 初始化配置文件
	 */
	protected void init() {
	    LoggerUtils.info(logger, "初始化配置文件-------");
		try {
			ConfigFileInfo configFile = getConfigFile();
			if(null == configFile) {
				LoggerUtils.error(logger, "初始化配置文件[失败]，[{}]文件未找到.", this.sysConfigPath);
				return;
			}
			try(InputStream in = configFile.inputStream()) {
                prop = new Properties();
                prop.load(in);
            }
			lastModifyTime  = configFile.lastModifyTime();
			LoggerUtils.info(logger, "初始化配置文件[结束]-------");
		} catch (Exception e) {
			LoggerUtils.error(logger, e);
		}
	}
	
	@Override
	public String getValue(String key) {
		String value = null;
		if(null != prop && StringUtils.isNotEmpty(key)){
			try {
				value = StringUtils.handleNull(prop.get(key));
				value = handleSysVar(value);
				value = handleVar(value);
			} catch (Exception e) {
				LoggerUtils.error(logger, e);
			}
		}
		return value;
	}
	
	/**
	 * 获取项目信息
	 * @return 获取项目信息对象
	 */
	public ProjectInfo getProjectInfo() {
		ProjectInfo projectInfo = null;
		if(null != prop){
			projectInfo = new ProjectInfo();
			projectInfo.initParam("project", this);
		}
		return projectInfo;
	}
	
	/**
	 * 重新初始化配置文件
	 */
	public void reInit() {
		init();
	}

    @Override
    public String getValue(String key, String defaultValue) {
        String value = getValue(key);
        if(StringUtils.isEmpty(value)) {
            value = defaultValue;
        }
        return value;
    }


	/**
	 * 获取配置对象通过配置类
	 * @param prefix 配置属性的前缀
	 * @param clazz 配置类
	 * @param <E> 类型
	 * @return 返回配置对象
	 */
	public <E> E getConfigObjByClass(String prefix, Class<E> clazz) {
		if(null == clazz || StringUtils.isEmpty(prefix)) {
			return null;
		}
		E obj = null;
		try {
			PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(clazz);
			if(propertyDescriptors.length == 0) {
				return null;
			}
			prefix = prefix.endsWith(".") ? prefix : (prefix + ".");
			obj = clazz.getDeclaredConstructor().newInstance();
			for(PropertyDescriptor pd : propertyDescriptors) {
				String name = pd.getName();
				String configKey = prefix + name;
				String value = getValue(configKey);
				if(StringUtils.isEmpty(value)) {
					continue;
				}
				Method writeMethod = pd.getWriteMethod();
				Type[] parameterTypes = writeMethod.getGenericParameterTypes();
				Type parameterType = parameterTypes[0];
				String typeName = parameterType.getTypeName();
				if(typeName.equals(String.class.getTypeName())) {
					writeMethod.invoke(obj, value);
				} else if(typeName.equals("int") || typeName.equals(Integer.class.getTypeName())) {
					int newValue = 0;
					try {
						newValue = Integer.parseInt(value);
					} catch (Exception ignore) {}
					writeMethod.invoke(obj, newValue);
				} else if(typeName.equals("boolean") || Objects.equals(typeName, Boolean.class.getTypeName())) {
					boolean newValue = false;
					try {
						newValue = Boolean.parseBoolean(value);
					} catch (Exception ignore) {}
					writeMethod.invoke(obj, newValue);
				} else if(typeName.equals("long") || Objects.equals(typeName, Long.class.getTypeName())) {
					long newValue = 0L;
					try {
						newValue = Long.parseLong(value);
					} catch (Exception ignore) {}
					writeMethod.invoke(obj, newValue);
				} else if(typeName.equals("double") || parameterType.equals(Double.class)) {
					double newValue = 0D;
					try {
						newValue = Double.parseDouble(value);
					} catch (Exception ignore) {}
					writeMethod.invoke(obj, newValue);
				} else if(typeName.equals("float") || parameterType.equals(Float.class)) {
					float newValue = 0F;
					try {
						newValue = Float.parseFloat(value);
					} catch (Exception ignore) {}
					writeMethod.invoke(obj, newValue);
				}
			}
		} catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
			LoggerUtils.error(logger, e);
		}

        return obj;
	}
	
}
